// Lab2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "HexIO/hexioctrl.h"
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

#include <iostream>
using namespace std;

#define DATA_REG_1 0x1F0
#define DATA_REG_2 0x170
#define STATE_REG_1 0x1F7
#define STATE_REG_2 0x177
#define COM_REG_1 0x1F7
#define COM_REG_2 0x177
#define DH_REG_1 0x1F6
#define DH_REG_2 0x176

#define IDENT_DEV_ATA 0xEC
#define IDENT_DEV_ATAPI 0xA1

#define wordsToRead 88

bool parseDev(int i_chanSel, int i_devSel, WORD* result, int i_bytesToRead);

void outP(int i_portSize, WORD w_portAddr, DWORD d_portVal);
DWORD inP(int i_portSize, WORD w_portAddr);
void decodePass(WORD *wa_data, int i_dataLength);

int _tmain(int argc, _TCHAR* argv[])
{
	ALLOW_IO_OPERATIONS

	WORD devicePass[wordsToRead];

	if (parseDev(0, 0, devicePass, wordsToRead))
	{
		cout << endl << "Primary Master:";
		decodePass(devicePass, wordsToRead);
	}

	if (parseDev(0, 1, devicePass, wordsToRead))
	{
		cout << endl << "Primary Slave:";
		decodePass(devicePass, wordsToRead);
	}

	if (parseDev(1, 0, devicePass, wordsToRead))
	{
		cout << endl << "Secondary Master:";
		decodePass(devicePass, wordsToRead);
	}

	if (parseDev(1, 1, devicePass, wordsToRead))
	{
		cout << endl << "Secondary Slave:";
		decodePass(devicePass, wordsToRead);
	}

	cin.get();
}

bool parseDev(int i_chanSel, int i_devSel, WORD* result, int i_bytesToRead)
{
	BYTE b_inPort;
	WORD w_inPort;
	bool isChanReady = false;
	bool prevStepValid = false;
	int numWords = 0;

	WORD w_dataReg, w_commReg, w_statReg, w_dhReg;
	BYTE b_devSel;

	if (i_chanSel == 0)
	{
		w_dataReg = DATA_REG_1;
		w_commReg = COM_REG_1;
		w_statReg = STATE_REG_1;
		w_dhReg = DH_REG_1;
	}
	else if (i_chanSel == 1)
	{
		w_dataReg = DATA_REG_2;
		w_commReg = COM_REG_2;
		w_statReg = STATE_REG_2;
		w_dhReg = DH_REG_2;
	}
	else
	{
		return false;
	}

	if (i_devSel == 0)
	{
		b_devSel = 0xA0;
	}
	else if (i_devSel == 1)
	{
		b_devSel = 0xB0;
	}

	//	select ATA channel, 0Ah value selects ATA channel 0
	outP(1, w_dhReg, b_devSel);

	//	set iteration limitation to prevent unlimited cycle
	int maxIters = int( (INT_MAX - 1) / 8 );

	//	check channel status
	while ( maxIters-- > 0 )
	{
		//	poll SR status byte
		b_inPort = (BYTE)inP(1, w_statReg);

		if (b_inPort == 0xFF) return false;

		//	check if 5th bit is set, if so, then ATA channel is broken, stop polling
		if ( (b_inPort & 0x20) > 0 )
		{
			return false;
		}

		//	if device ready, terminate polling
		if ( (b_inPort & 0x80) == 0 ) 
		{
			isChanReady = true;
//			cout << "Device status: 0x" << std::hex << int(b_inPort) << endl;
			break;
		}
	}

	if (isChanReady)
	{
		prevStepValid = false;

		//	select ATA channel, 0Ah value selects ATA channel 0
		outP(1, w_dhReg, i_devSel);

		//	read status register in second time
		maxIters = 10000;//int( (INT_MAX - 1) / 1000000 );

		while (maxIters-- > 0)
		{
			b_inPort = (BYTE)inP(1, w_statReg);

			if ( (b_inPort & 0x40) > 0)
			{
//				cout << "Device is ready to execute commands" << endl;
				prevStepValid = true;
				break;
			}
		}

		//	request device passport
		if (prevStepValid)
		{
			prevStepValid = false;

//			cout << "Sending device passport request..." << endl;

			outP(1, w_commReg, IDENT_DEV_ATAPI);

			bool moreBytes = true;
			int curCount = 0;

			while ( moreBytes && (curCount < i_bytesToRead) )
			{
				moreBytes = false;

				//	poll DRQ bit to wait for previous operation completion
				maxIters = 1000000;
				prevStepValid = false;

				while (maxIters-- > 0)
				{
					b_inPort = (BYTE)inP(1, w_statReg);

					//	check if DRQ bit in active state, if so, than we can read data word from DR port
					if ( (b_inPort & 0x8) > 0)
					{
						prevStepValid = true;
						break;
					}
				}

				//	if we've got device readiness signal, proceed work
				if (prevStepValid)
				{
					w_inPort = (WORD)inP(2, w_dataReg);

					numWords++;
					result[curCount++] = w_inPort;
					moreBytes = true;
				}
				else
				{
					//	TO DO: check ERR register, and if it has been an error, repeat passport's word request some limited
					//	number times, if it will be unsuccessfull anyway, terminate program
					return false;
				}
			}
		}
		else
		{
			//	TO DO: code some fault action here
			cout << "Device can't execute any commands, pls try again later" << endl;
		}
	}

	return true;
}

DWORD inP(int i_portSize, WORD w_portAddr)
{
	DWORD d_result;
	WORD w_result;
	BYTE b_result;

	switch (i_portSize)
	{
		case 1:

			__asm
			{
				mov dx, w_portAddr
				xor al, al
				in al, dx
				mov b_result, al
			}

			d_result = (DWORD)(b_result);

			break;

		case 2:

			__asm
			{
				mov dx, w_portAddr
				xor ax, ax
				in ax, dx
				mov w_result, ax
			}

			d_result = (DWORD)(w_result);

			break;

		case 4:
		default:
			
			__asm
			{
				mov dx, w_portAddr
				xor eax, eax
				in eax, dx
				mov d_result, eax
			}
	}

	return d_result;
}
void outP(int i_portSize, WORD w_portAddr, DWORD d_portVal)
{
	BYTE b_portVal;
	WORD w_portVal;

	switch (i_portSize)
	{
		case 1:

			b_portVal = (BYTE)(d_portVal);
			__asm
			{
				mov dx, w_portAddr
				mov al, b_portVal
				out dx, al
			}
			
			break;

		case 2:

			w_portVal = (WORD)(d_portVal);
			__asm
			{
				mov dx, w_portAddr
				mov ax, w_portVal
				out dx, ax
			}

			break;

		case 4:
		default:

			__asm
			{
				mov dx, w_portAddr
				mov eax, d_portVal
				out dx, eax
			}

			break;
	}
}

void decodePass(WORD *wa_data, int i_dataLength)
{
	int counter = 0;
	bool hasAny = false;

	cout << endl;

	cout << "Device: ";
	
		for (counter = 0; counter < 20; counter++)
		{
			cout << (char)(wa_data[27 + counter] >> 8) << (char)(wa_data[27 + counter] & 0xFF);
		}

	cout << endl;

	cout << "Device with changeable storage: " << (( (wa_data[0] & 0x40) > 0 ) ? "yes" : "no") << endl;
	cout << "Device type: " << (( (wa_data[0] & 0x80) > 0 ) ? "ATAPI" : "ATA") << endl;

	cout << "Serial No: ";

		for (counter = 0; counter < 10; counter++)
		{
			cout << (char)(wa_data[10 + counter] >> 8) << (char)(wa_data[10 + counter] & 0xFF);
		}

	cout << endl;

	cout << "Firmware version: ";
		
		for (counter = 0; counter < 4; counter++)
		{
			cout << (char)(wa_data[23 + counter] >> 8) << (char)(wa_data[23 + counter] & 0xFF);
		}
	
	cout << endl;

	cout << "PIO supported:";

		hasAny = false;
		if ((wa_data[53] & 0x2) > 0)
		{
			if (hasAny |= ((wa_data[64] & 0x1) > 0)) cout << " PIO_3";
			if (hasAny |= ((wa_data[64] &0x2) > 0)) cout << " PIO_4";
		}
		if (!hasAny) cout << " none";

	cout << endl;

	cout << "Multiword DMA supported:";

		hasAny = false;
		if ( hasAny |= ((wa_data[63] & 0x1) > 0) ) cout << " MWDMA_0";
		if ( hasAny |= ((wa_data[63] & 0x2) > 0) ) cout << " MWDMA_1";
		if ( hasAny |= ((wa_data[63] & 0x4) > 0) ) cout << " MWDMA_2";
		if ( !hasAny ) cout << " none";

	cout << endl;

	cout << "Ultra DMA supported:";

		hasAny = false;
		//	if byte 88 is actual
		if ((wa_data[53] & 0x4) > 0)
		{
			if (hasAny |= ((wa_data[88] & 0x1) > 0)) cout << " UDMA_0";
			if (hasAny |= ((wa_data[88] & 0x2) > 0)) cout << " UDMA_1";
			if (hasAny |= ((wa_data[88] & 0x4) > 0)) cout << " UDMA_2";
			if (hasAny |= ((wa_data[88] & 0x8) > 0)) cout << " UDMA_3";
			if (hasAny |= ((wa_data[88] & 0x10) > 0)) cout << " UDMA_4";
			if (hasAny |= ((wa_data[88] & 0x20) > 0)) cout << " UDMA_5";
			if (hasAny |= ((wa_data[88] & 0x40) > 0)) cout << " UDMA_6";
		}
		if (!hasAny) cout << " none";

	cout << endl;

	cout << "Current modes:";

		hasAny = false;
		if (hasAny |= ((wa_data[63] & 0x100) > 0)) cout << " MWDMA_0";
		if (hasAny |= ((wa_data[63] & 0x200) > 0)) cout << " MWDMA_1";
		if (hasAny |= ((wa_data[63] & 0x400) > 0)) cout << " MWDMA_1";

		if ((wa_data[53] & 0x4) > 0)
		{
			if (hasAny |= ((wa_data[88] & 0x100) > 0)) cout << " UDMA_0";
			if (hasAny |= ((wa_data[88] & 0x200) > 0)) cout << " UDMA_1";
			if (hasAny |= ((wa_data[88] & 0x400) > 0)) cout << " UDMA_2";
			if (hasAny |= ((wa_data[88] & 0x800) > 0)) cout << " UDMA_3";
			if (hasAny |= ((wa_data[88] & 0x1000) > 0)) cout << " UDMA_4";
			if (hasAny |= ((wa_data[88] & 0x2000) > 0)) cout << " UDMA_5";
			if (hasAny |= ((wa_data[88] & 0x4000) > 0)) cout << " UDMA_6";
		}

		if (!hasAny) cout << " PIO";

	cout << endl;

	cout << "ATA version: ";

		if ((wa_data[81] == 0x0) || (wa_data[81] == 0xFFFF)) cout << "deprecated";
		else cout << wa_data[81];
	
	cout << endl;
}